package com.uuah.server.context.notification;

import java.util.Collection;
import java.util.Map;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.resource.spi.work.Work;
import javax.resource.spi.work.WorkException;
import javax.resource.spi.work.WorkListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.uuah.api.lifecycle.IDisposable;
import com.uuah.api.work.IWorkManager;
import com.uuah.exception.lifecycle.LifecycleException;
import com.uuah.server.api.context.notification.IBlockingServerEvent;
import com.uuah.server.api.context.notification.IServerNotificationHandler;
import com.uuah.server.api.context.notification.linstener.IServerNotificationListener;
import com.uuah.utils.ClassUtils;

/**
 *
 * TODO comment ...
 *
 * <p>
 * update record:updator，update time，update content and version
 * </p>
 *
 * @author <a href="jonny_quan@hotmail.com">jonny</a>
 * @date 2009 2009-3-27 下午01:34:13
 * @version 1.0.0
 */
public class ServerNotificationManager implements Work, IDisposable,
		IServerNotificationHandler {

	protected transient Logger logger = LoggerFactory.getLogger(getClass());
	public static final String NULL_SUBSCRIPTION = "NULL";
	private EventConfiguration eventConfiguration = new EventConfiguration();
	private AtomicBoolean disposed = new AtomicBoolean(false);
	private BlockingDeque eventQueue = new LinkedBlockingDeque();

	public void start(IWorkManager workManager, WorkListener workListener)
			throws LifecycleException {
		try {
			workManager.scheduleWork(this, IWorkManager.INDEFINITE, null,
					workListener);
		} catch (WorkException e) {
			throw new LifecycleException(e, this);
		}
	}

	public void addInterfaceToType(Class iface, Class event) {
		eventConfiguration.addInterfaceToType(iface, event);
	}

	public void setInterfaceToTypes(Map interfaceToEvents)
			throws ClassNotFoundException {
		eventConfiguration.addAllInterfaceToTypes(interfaceToEvents);
	}

	public void addListenerSubscription(IServerNotificationListener listener,
			String subscription) {
		eventConfiguration
				.addListenerSubscriptionPair(new ListenerSubscriptionPair(
						listener, subscription));
	}

	public void removeListener(IServerNotificationListener listener) {
		eventConfiguration.removeListener(listener);
	}

	public void removeAllListeners(Collection listeners) {
		eventConfiguration.removeAllListeners(listeners);
	}

	public void fireNotification(ServerNotification notification) {
		if (!disposed.get()) {
			if (notification instanceof IBlockingServerEvent) {
				notifyListeners(notification);
			} else {
				try {
					eventQueue.put(notification);

				} catch (InterruptedException e) {
					if (!disposed.get()) {
						logger.error("Failed to queue notification: "
								+ notification, e);
					}
				}
			}
		} else {
			logger
					.warn("Notification not enqueued after ServerNotificationManager disposal: "
							+ notification);
		}
	}

	public boolean isNotificationEnabled(Class type) {
		boolean enabled = false;
		if (eventConfiguration != null) {
			EventPolicy policy = eventConfiguration.getPolicy();
			if (policy != null) {
				enabled = policy.isNotificationEnabled(type);
			}
		}
		return enabled;
	}

	public void dispose() {
		disposed.set(true);
		eventConfiguration = null;
	}

	protected void notifyListeners(ServerNotification notification) {
		if (!disposed.get()) {
			eventConfiguration.getPolicy().dispatch(notification);
		} else {
			logger
					.warn("Notification not delivered after ServerNotificationManager disposal: "
							+ notification);
		}
	}

	public void release() {
		dispose();
	}

	public void run() {
		while (!disposed.get()) {
			try {
				ServerNotification notification = (ServerNotification) eventQueue
						.take();
				notifyListeners(notification);
			} catch (InterruptedException e) {
			}
		}
	}

	public static Class toClass(Object value) throws ClassNotFoundException {
		Class clazz;
		if (value instanceof String) {
			clazz = ClassUtils.loadClass(value.toString(), value.getClass());
		} else if (value instanceof Class) {
			clazz = (Class) value;
		} else {
			throw new IllegalArgumentException(
					"Notification types and listeners must be a Class with fully qualified class name. Value is: "
							+ value);
		}
		return clazz;
	}

	EventPolicy getPolicy() {
		return eventConfiguration.getPolicy();
	}
}
